package com.innovation.ic.b1b.framework.manager;

import com.innovation.ic.b1b.framework.util.DateUtils;
import com.jcraft.jsch.*;
import org.apache.commons.io.IOUtils;
import org.apache.poi.ss.usermodel.DateUtil;

import java.io.*;
import java.nio.file.Files;
import java.text.DateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author linuo
 * @desc SftpChannal的管理类
 * @time 2022年7月7日16:49:07
 */
public class SftpChannelManager {
    private ThreadLocal<ChannelSftp> channelSftpThreadLocal = new ThreadLocal<>();
    private ThreadLocal<ChannelExec> channelExecThreadLocal = new ThreadLocal<>();

    private JSch jsch;
    private Session sshSession;
    private Properties sshConfig;
    private ChannelSftp channelSftp;
    private ChannelExec channelExec;
    private ChannelShell channelShell;

    private final static int TIMEOUT = 60000;

    public SftpChannelManager(String username, String password, String host, int port, int timeout) {
        try {
            jsch = new JSch();
            sshSession = jsch.getSession(username, host, port);
            sshSession.setPassword(password);

            sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");

            sshSession.setConfig(sshConfig);
            sshSession.connect();

            channelSftp = (ChannelSftp) sshSession.openChannel("sftp");
            channelExec = (ChannelExec) sshSession.openChannel("exec");
            channelShell = (ChannelShell) sshSession.openChannel("shell");

            channelSftp.connect(timeout);
            channelExec.connect(timeout);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public SftpChannelManager(String username, String password, String host, int port) {
        this(username, password, host, port, TIMEOUT);
    }

    /**
     * 获取sftp连接
     * @return 返回sftp连接
     */
    public ChannelSftp getChannelSftp() {
        return channelSftp;
    }

    /**
     * @Description: 获取count数个sftp连接
     * @Params:
     * @Return:
     * @Author: Mr.myq
     * @Date: 2022/11/19:47
     */
    public void tryLoginSetThreadLocal() {
        channelSftpThreadLocal.set(this.channelSftp);
        channelExecThreadLocal.set(this.channelExec);
    }

    /**
     * @Description: 删除元素，防止内存泄漏
     * @Params:
     * @Return:
     * @Author: Mr.myq
     * @Date: 2022/11/19:54
     */
    public void remove() {
        channelSftpThreadLocal.remove();
        channelExecThreadLocal.remove();
    }

    /**
     * 上传文件
     *
     * @param directory 图片上传路径
     * @param bytes     需要转换成文件的byte数组
     * @param fileName  文件名
     */
    public void upload(String directory, byte[] bytes, String fileName) {
        InputStream io = null;
        try {
            String home = channelSftp.getHome();
            String pwd = channelSftp.pwd();
            if (!pwd.equals(directory)) {
                channelSftp.cd(directory);
            }
            io = new ByteArrayInputStream(bytes);
            channelSftp.put(io, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != io) {
                try {
                    io.close();
                    io = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 复制文件
     *
     * @param directory   源路径
     * @param destination 目标路径
     * @param fileName    文件名
     */
    public void copy(String directory, String destination, String fileName) throws JSchException, IOException {
//        //正常命令
//        StringBuilder runLog = new StringBuilder("");
//        //错误命令
//        StringBuilder errLog = new StringBuilder("");
//        //记录命令
//        BufferedReader inputStreamReader = null;
//        //记录命令错误
//        BufferedReader errInputStreamReader = null;
        // Execute a command on the remote machine.
        try {
//            synchronized (SftpFileUtil.class){
            String dos = "mv " + directory + "/" + fileName + " " + destination;
//                    + channelExec.isClosed());
            channelExec.setCommand(dos);
            channelExec.connect(); // 执行命令
//            }

//            //记录命令执行 log
//            String line = null;
//            while ((line = inputStreamReader.readLine()) != null) {
//                runLog.append(line).append("\n");
//            }
//            //记录命令执行错误 log
//            String errLine = null;
//            while ((errLine = errInputStreamReader.readLine()) != null) {
//                errLog.append(errLine).append("\n");
//            }
//            channelExec.setCommand("");
//            channelExec.disconnect();
//            LOGGER.info("输出 shell 命令执行日志:" + "exitStatus=" + channelExec.getExitStatus() + ", openChannel.isClosed="
//                    + channelExec.isClosed());
//            LOGGER.info("命令执行完成，执行日志如:" + runLog.toString());
//            LOGGER.info("命令执行完成，执行错误日志如:" + errLog.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 执行脚本
     *
     * @param directory 脚本路径
     * @param fileName  脚本名称
     */
    public String execScript(String directory, String fileName) {
        BufferedReader inputStreamReader = null;
        StringBuilder runLog = new StringBuilder();
        try {
            // 1. 组装执行脚本
            String cmd = "sh " + directory + "/" + fileName;

            // 2. 通过 exec 方式执行 shell 命令
            channelExec.setCommand(cmd);
            // 3. 执行命令
            channelExec.connect();

            // 4. 获取标准输入流
            inputStreamReader = new BufferedReader(new InputStreamReader(channelExec.getInputStream()));

            // 5. 记录命令执行 log
            String line;
            while ((line = inputStreamReader.readLine()) != null) {
                runLog.append(line).append("\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStreamReader != null) {
                    inputStreamReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return runLog.toString();
    }

    /**
     * 执行命令
     *
     * @param command 脚本命令
     * @return 返回脚本执行日志
     */
    public List<String> execCommand(String command) throws IOException {
        PrintWriter printWriter = null;
        BufferedReader input = null;
        List<String> list = new ArrayList<>();
        try {
            channelShell.connect();
            input = new BufferedReader(new InputStreamReader(channelShell.getInputStream()));
            printWriter = new PrintWriter(channelShell.getOutputStream());
            printWriter.println(command);
            printWriter.println("exit");
            printWriter.flush();
            String line;
            while ((line = input.readLine()) != null) {
                list.add(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (printWriter != null) {
                printWriter.close();
            }
            if (input != null) {
                input.close();
            }
        }
        return list;
    }

    /**
     * 关闭链接
     */
    public void closeChannel() {
        if (channelExec != null) {
            try {
                channelExec.disconnect();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void closeSession() {
        if (sshSession != null) {
            sshSession.disconnect();
        }
    }

    public boolean deleteDirFiles(String newsFile) {
        try {
            channelSftp.cd(newsFile);
            ListIterator a = channelSftp.ls(newsFile).listIterator();
            while (a.hasNext()) {
                ChannelSftp.LsEntry oj = (ChannelSftp.LsEntry) a.next();
                delete(newsFile, oj.getFilename());
            }
        } catch (Exception e) {
            e.getMessage();
        }
        return false;
    }

    /**
     * 上传本地文件到sftp指定的服务器，
     *
     * @param directory      目标文件夹
     * @param uploadFile     本地文件夹
     * @param remoteFileName 重命名的文件名字
     * @param isRemote       是否需要重命名  是true 就引用remoteFileName 是false就用默认的文件名字
     */
    public void upload(String directory, String uploadFile, String remoteFileName, boolean isRemote) {
        FileInputStream io = null;
        try {
            boolean isExist = false;
            try {
                SftpATTRS sftpATTRS = channelSftp.lstat(directory);
                isExist = true;
                isExist = sftpATTRS.isDir();
            } catch (Exception e) {
                e.printStackTrace();
                if (e.getMessage().toLowerCase().equals("no such file")) {
                    isExist = false;
                }
            }
            if (!isExist) {
                boolean existDir = isExistDir(directory);
                if (!existDir) {
                    String pathArry[] = directory.split("/");
                    StringBuffer Path = new StringBuffer("/");
                    for (String path : pathArry) {
                        if (path.equals("")) {
                            continue;
                        }
                        Path.append(path + "/");
                        if (!isExistDir(Path + "")) {
                            // 建立目录
                            channelSftp.mkdir(Path.toString());
                            // 进入并设置为当前目录
                        }
                        channelSftp.cd(Path.toString());
                    }
                }
            }
            channelSftp.cd(directory);
            File file = new File(uploadFile);
            io = new FileInputStream(file);
            if (isRemote) {
                channelSftp.put(io, remoteFileName);
            } else {
                channelSftp.put(io, file.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != io) {
                try {
                    io.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public boolean isExistDir(String path) {
        boolean isExist = false;
        try {
            SftpATTRS sftpATTRS = channelSftp.lstat(path);
            isExist = true;
            return sftpATTRS.isDir();
        } catch (Exception e) {
            e.printStackTrace();
            if (e.getMessage().toLowerCase().equals("no such file")) {
                isExist = false;
            }
        }
        return isExist;

    }

    /**
     * 上传
     */
    public List<String> uploadZip(String directory, String uploadFile, List<String> filePath, String remoteName) {
        try {
            List<String> list = new ArrayList<>();
            boolean existDir = isExistDir(directory);
            if (!existDir) {
                channelSftp.mkdir(directory);
            }
            channelSftp.cd(directory);
            int i = 1;
            for (String newPath : filePath) {
                FileInputStream io = null;
                try {
                    File file = new File(uploadFile + newPath);
                    io = new FileInputStream(file);
                    channelSftp.put(io, remoteName + "-" + i + ".jpg");
                    io.close();
                    list.add(remoteName + "-" + i + ".jpg");
                    i++;
                } catch (Exception ex) {
                    ex.printStackTrace();
                } finally {
                    if (null != io) {
                        try {
                            io.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            return list;
        } catch (SftpException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 查看
     */
    public List<String> check(String directory) {
        List<String> fileList = new ArrayList<>();
        try {
            channelSftp.cd(directory);
            ListIterator a = channelSftp.ls(directory).listIterator();
            while (a.hasNext()) {
                ChannelSftp.LsEntry oj = (ChannelSftp.LsEntry) a.next();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return fileList;
    }

    /**
     * 递归创建目录
     *
     * @param originDirectory /opt/image/cyz/test/202205/23
     */
    private void mkdirRecursion(String originDirectory) throws SftpException {
        // 获取从浅到深的目录
        List<String> dirs = new ArrayList<>();
        String[] words = originDirectory.trim().split("/");
        String lastDir = "";
        for (String word : words) {
            lastDir += "/" + word;
            dirs.add(lastDir);
        }

        // 由浅到深进入目录和创建目录
        for (String dir : dirs) {
            try {
                channelSftp.cd(dir);
            } catch (SftpException e) {
                channelSftp.mkdir(dir);
                channelSftp.cd(dir);
                e.printStackTrace();
            }
        }
    }

    /**
     * 将输入流的数据上传到sftp作为文件
     *
     * @param directory    上传到该目录
     * @param sftpFileName sftp端文件名
     * @param input        输入流
     * @throws SftpException
     * @throws Exception
     */
    public void upload(String directory, String sftpFileName, InputStream input) throws SftpException {
        mkdirRecursion(directory);
        channelSftp.put(input, sftpFileName);
    }

    public void personalityUpload(String directory, String sftpFileName, InputStream input) throws SftpException {
        ChannelSftp channelSftp = null;
        try {
            channelSftp = channelSftpThreadLocal.get();
            personalityMkdirRecursion(channelSftp, directory);
            channelSftp.put(input, sftpFileName);
        } catch (SftpException e) {
            e.printStackTrace();
            throw new RuntimeException("从ThreadLocal中获取sftp连接错误");
        } finally {
            if (channelSftp != null) {
                this.remove();
            }
        }
    }

    /**
     * @Description: 个性的上传方法
     * @Params:
     * @Return:
     * @Author: Mr.myq
     * @Date: 2022/11/110:26
     */
    public void personalityUpload(String directory, String sftpFileName, byte[] byteArr) throws SftpException {
        // 尝试获取sftp并设置local
        this.tryLoginSetThreadLocal();
        personalityUpload(directory, sftpFileName, new ByteArrayInputStream(byteArr));
    }

    private void personalityMkdirRecursion(ChannelSftp channelSftp, String originDirectory) throws SftpException {
        // 获取从浅到深的目录
        List<String> dirs = new ArrayList<>();
        String[] words = originDirectory.trim().split("/");
        String lastDir = "";
        for (String word : words) {
            lastDir += "/" + word;
            dirs.add(lastDir);
        }
        // 由浅到深进入目录和创建目录
        for (String dir : dirs) {
            try {
                channelSftp.cd(dir);
            } catch (SftpException e) {
                channelSftp.mkdir(dir);
                channelSftp.cd(dir);
                e.printStackTrace();
            }
        }
    }


    /**
     * 上传单个文件
     *
     * @param directory  上传到sftp目录
     * @param uploadFile 要上传的文件,包括路径
     * @throws FileNotFoundException
     * @throws SftpException
     * @throws Exception
     */
    public void upload(String directory, String uploadFile) throws FileNotFoundException, SftpException {
        File file = new File(uploadFile);
        upload(directory, file.getName(), new FileInputStream(file));
    }

    /**
     * 将byte[]上传到sftp，作为文件。注意:从String生成byte[]是，要指定字符集。
     *
     * @param directory    上传到sftp目录
     * @param sftpFileName 文件在sftp端的命名
     * @param byteArr      要上传的字节数组
     * @throws SftpException
     * @throws Exception
     */
    public void upload(String directory, String sftpFileName, byte[] byteArr) throws SftpException {
        upload(directory, sftpFileName, new ByteArrayInputStream(byteArr));
    }

    /**
     * 将字符串按照指定的字符编码上传到sftp
     *
     * @param directory    上传到sftp目录
     * @param sftpFileName 文件在sftp端的命名
     * @param dataStr      待上传的数据
     * @param charsetName  sftp上的文件，按该字符编码保存
     * @throws UnsupportedEncodingException
     * @throws SftpException
     * @throws Exception
     */
    public void upload(String directory, String sftpFileName, String dataStr, String charsetName) throws UnsupportedEncodingException, SftpException {
        upload(directory, sftpFileName, new ByteArrayInputStream(dataStr.getBytes(charsetName)));
    }

    /**
     * 下载文件
     *
     * @param directory    下载目录
     * @param downloadFile 下载的文件
     * @param saveFile     存在本地的路径
     * @throws SftpException
     * @throws FileNotFoundException
     * @throws Exception
     */
    public void download(String directory, String downloadFile, String saveFile) throws SftpException, FileNotFoundException {
        if (directory != null && !"".equals(directory)) {
            channelSftp.cd(directory);
        }
        File file = new File(saveFile);
        channelSftp.get(downloadFile, new FileOutputStream(file));
    }


    /**
     * 从sftp服务器上下载文件
     * @param channelSftp sftp通道
     * @param remoteFile 远程文件
     * @param localFile 本地文件
     */
    public void download(ChannelSftp channelSftp, String remoteFile, String localFile){
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(localFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        try {
            channelSftp.get(remoteFile, outputStream);
        } catch (SftpException e) {
            e.printStackTrace();
        }
    }


    /**
     * 下载文件
     *
     * @param directory    下载目录
     * @param downloadFile 下载的文件名
     * @return 字节数组
     * @throws SftpException
     * @throws IOException
     * @throws Exception
     */
    public byte[] download(String directory, String downloadFile) throws SftpException, IOException {
        if (directory != null && !"".equals(directory)) {
            channelSftp.cd(directory);
        }
        InputStream is = channelSftp.get(downloadFile);
        byte[] fileData = IOUtils.toByteArray(is);
        return fileData;
    }

    /**
     * 删除文件
     *
     * @param directory  要删除文件所在目录
     * @param deleteFile 要删除的文件
     * @throws SftpException
     * @throws Exception
     */
    public void delete(String directory, String deleteFile) throws SftpException {
        channelSftp.cd(directory);
        channelSftp.rm(deleteFile);
    }

    /**
     * 列出目录下的文件
     *
     * @param directory 要列出的目录
     * @param directory
     * @return
     * @throws SftpException
     */
    public Vector<ChannelSftp.LsEntry> listFiles(String directory) throws SftpException {
        return channelSftp.ls(directory);
    }

    /**
     * @param sftpChannelManager
     * @param dir
     * @param res  * @return List<String>
     * @Description:  返回所有文件
     * @Author: myq
     * @Date: 2023/5/10 11:04
     */
    public  List<String> lsFile(SftpChannelManager sftpChannelManager, String dir, List<String> res) throws SftpException {
        String tar = dir;
        Vector<ChannelSftp.LsEntry> objects = sftpChannelManager.listFiles(dir);
        for (ChannelSftp.LsEntry e : objects) {
            String filename = e.getFilename();
            if (".".equals(filename) || "..".equals(filename)) {
                continue;
            }
            SftpATTRS sftpATTRS = e.getAttrs();
            if (sftpATTRS.isDir()) {
                tar = dir + "/" + filename;
                lsFile(sftpChannelManager, tar, res);
            } else {
                String absFile = tar + "/" + filename;
                String p = LocalDate.now().toString();
                Pattern compile = Pattern.compile(".+("+p+")\\.\\w.log");
                Matcher matcher = compile.matcher(absFile);
                while(matcher.find()){
                    res.add(absFile);
                }

            }
        }
        return res;
    }


    public static void main(String[] args) throws SftpException, IOException {
        SftpChannelManager sftpChannelManager = new SftpChannelManager("root", "Ydec2022", "192.168.80.110", 22);

        List<String> strings = sftpChannelManager.lsFile(sftpChannelManager, "/opt/logs", new ArrayList<String>());

        String absFile = strings.get(0);
        int index = absFile.lastIndexOf("/");
        String dirPath = "./temp/";
        File file = new File(dirPath);
        if(!file.exists())
            file.mkdirs();
        String localFile = dirPath + absFile.substring(index + 1);
        sftpChannelManager.download(sftpChannelManager.channelSftp,absFile, localFile);


        File f = new File(localFile);
        BufferedReader bs = new BufferedReader(new InputStreamReader(Files.newInputStream(f.toPath())));
        while(bs.ready()){
            String s = bs.readLine();

            String p = LocalDate.now().toString();
            Pattern compile = Pattern.compile("(2023-05-04).+(WARN).+");
            Matcher matcher = compile.matcher(s);
            while(matcher.find()){
                System.out.println(matcher.group());
            }
        }
        sftpChannelManager.closeSession();

    }

}